package com.wx.task.atomic;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;


import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * 秘钥
 * riLy1xRUrHVolOXJv3z4Vg==
 */

public class RSAUtils {
    public static final String KEY_ALGORITHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
    private static final String PUBLIC_KEY = "RSAPublicKey";
    private static final String PRIVATE_KEY = "RSAPrivateKey";
    private static final int MAX_ENCRYPT_BLOCK = 117;
    private static final int MAX_DECRYPT_BLOCK = 128;

    public RSAUtils() {
    }

    public static Map<String, Key> genKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();
        Map<String, Key> keyMap = new HashMap(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    /**
     * 数字签名
     * @param data
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateK);
        signature.update(data);
        return new String(Base64.encodeBase64(signature.sign()));
    }

    /**
     * 验证签名
     * @param data
     * @param publicKey
     * @param sign
     * @return
     * @throws Exception
     */
    public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicK);
        signature.update(data);
        return signature.verify(Base64.decodeBase64(sign));
    }

    /**
     * 私钥解密
     * @param encryptedData
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(2, privateK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;

        for(int i = 0; inputLen - offSet > 0; offSet = i * MAX_DECRYPT_BLOCK) {
            byte[] cache;
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }

            out.write(cache, 0, cache.length);
            ++i;
        }

        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

    /**
     * 公钥解密
     * @param encryptedData
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(2, publicK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;

        for(int i = 0; inputLen - offSet > 0; offSet = i * MAX_DECRYPT_BLOCK) {
            byte[] cache;
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }

            out.write(cache, 0, cache.length);
            ++i;
        }

        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

    /**
     * 公钥加密
     * @param data
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(1, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;

        for(int i = 0; inputLen - offSet > 0; offSet = i * MAX_ENCRYPT_BLOCK) {
            byte[] cache;
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }

            out.write(cache, 0, cache.length);
            ++i;
        }

        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }

    /**
     * 私钥加密
     * @param data
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(1, privateK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;

        for(int i = 0; inputLen - offSet > 0; offSet = i * MAX_ENCRYPT_BLOCK) {
            byte[] cache;
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }

            out.write(cache, 0, cache.length);
            ++i;
        }

        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }

    /**
     * 获取私钥
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPrivateKey(Map<String, Key> keyMap) throws Exception {
        Key key = keyMap.get(PRIVATE_KEY);
        return new String(Base64.encodeBase64(key.getEncoded()));
    }

    /**
     * 获取公钥
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPublicKey(Map<String, Key> keyMap) throws Exception {
        Key key = keyMap.get(PUBLIC_KEY);
        return new String(Base64.encodeBase64(key.getEncoded()));
    }


    /**
     * 将参数名称按照ASCII排序
     * @param paramMap
     * @return
     */
    public static String sortParam(Map<String, Object> paramMap) {
        List<Map.Entry<String, Object>> list = new ArrayList<Map.Entry<String, Object>>(paramMap.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<String, Object>>() {
            @Override
            public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
                return (o1.getKey().compareTo(o2.getKey()));
            }
        });
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Object> item : list) {
            if (!org.apache.commons.lang3.StringUtils.isEmpty(item.getKey()) && !org.apache.commons.lang3.StringUtils.isEmpty(item.getValue().toString())) {
                sb.append(item.getKey()).append("=").append(item.getValue()).append("&");
            }
        }
        return sb.substring(0, sb.length() - 1);
    }

    public static void main(String[] args) throws Exception {
        Map<String, Key> map = genKeyPair();
        String privateKey = getPrivateKey(map);
        String publicKey = getPublicKey(map);
        System.out.println("pub=" + publicKey);
        System.out.println("pri="+ privateKey);
    }

}
/**
 * pub=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCdU4sSbIFDOZvJilu+Zz0hZMeV/ITJ7KTLJtW2UmFXus3mqfsfy5xBL9jrEZHEphuvXk82hXSkvSAxoyjFObkn+a1E1zoAgKGwjZP2KzNQfubEalOwiXjS2+itH5Uhaghwn6jaLeBY/3Bl0RS35dZcUqdoJ4iZqiafaSYGTejQewIDAQAB
 * pri=MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAJ1TixJsgUM5m8mKW75nPSFkx5X8hMnspMsm1bZSYVe6zeap+x/LnEEv2OsRkcSmG69eTzaFdKS9IDGjKMU5uSf5rUTXOgCAobCNk/YrM1B+5sRqU7CJeNLb6K0flSFqCHCfqNot4Fj/cGXRFLfl1lxSp2gniJmqJp9pJgZN6NB7AgMBAAECgYBeY/lUnHUReEbXrINOwXITzvtQKakh3TEiEbK3KMXudM2ETwe0E90z3xkuL+RdL6v8k4DDHV7WWAIwxct4SSGkv9+exSvuf1pCWAH1MLeMbua5wywuWRzzGgy1FtmVLF/pAJNfst0WR8JbFys+UgvVggmLDEUe+QmkTE+SRJ0/AQJBAOFisG/FuwmCgxwQI5chasleJ1XkkIZoUj567o+/Ko3ydihq2k5k+jniNzL7Y9NjMRdmkfo4S1Jvf7PcXG0GJhECQQCysj0T5Dsu39SfencmqNwIBDVC2wlEq7CKQzI4ce8cvi3sTUI9xwr8mwdV0K9/62KPYKCzEWrzFQ0ggaop6ZHLAkEAtW1u35qIlAAmMCnhrF9vCYesHF0v5bIGOltUvAvDYp5BdQmGj8KDFqM9keGBGwFm3BJqAcTdBQ/R+WOyVuSzMQJBAI8cW/oAQL2rmNcX9zDI716kMBuY2/VZIwu4ARJffMYiMRkcTr63F79yUQ+TD9p8ovR8yHZgrqx64YZ1Edh7LL0CQQCmR5sdzk50vvqAS8lj3UGJdOpBJ7XtiW0s3vkop9+L5NLvM3nVxyFzKaxuCkstO/d5xi9bm6HUN/d1gPyA5PCP
 */
/**
 * prod
 * pub=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqqa44fcG3MT+2t/09jHK4Bv2ttXcWTDr0S8kKsOP/e+jCUAxI1pjkicp5njW0NvdkEA32pY6JLVoDGYbEaehwcjFV/3IBsn2vcmjraG4+Le1BrbIcc4TFkeHJnPMDYeIzCZToolAAWqspRuicsDEThzuktybQCSlKK5ZZZpjnjwIDAQAB
 * pri=MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKqprjh9wbcxP7a3/T2McrgG/a21dxZMOvRLyQqw4/976MJQDEjWmOSJynmeNbQ292QQDfaljoktWgMZhsRp6HByMVX/cgGyfa9yaOtobj4t7UGtshxzhMWR4cmc8wNh4jMJlOiiUABaqylG6JywMROHO6S3JtAJKUorlllmmOePAgMBAAECgYAHKUOUP0KnqztfMW1A7enKaxCt/1VV7DD1G1D+T9nhWJO1w6IgVFDqFhYW8FqUi1HqMvpDBUNr4ilKxAFxsHf+XhdelfJS0EsSudWvrSswS372SilJc2fcJO6tUVQNV3IuMrTyYBuNWMfaXPl0WJwgCZFuiGZXGQkb2ZqXmdpiMQJBAPqlqma+BpSBtAIFSWkahYfFCi1OC+9WkG7q/8BSB3hJlYtqWFNoOibx6c9u/F3pVuYRjsxgAXENJ/qPqREOmNMCQQCuTrnPWxn3RN7eYmACaV5Jgfn0+uhsOipDp9L7909eYGviyLeuPLlTE6A0R8rzeEUhHufCeuQ/1ynx3owsZUDVAkAM9CDhXHLEnAtsi09m8irygFltS/pL7UJbqSN8kgC20765zM4xyVIwDxKRWOAMDnIiQ8gIgfGxIQfu6BvJuOJnAkB2xqqTA68z8bg0rbBitE5NpMYhC4CpjxERvSBmFeX9l5KsnL2dez+Fvs/iEDHZqlDg+uBOg9NZNVMdv9qgvfZpAkEA+A9MUU43pnmZXxZ5u1fhsFdKLidA5bg+pN32UdgZFGPOeThEZjV0bD1JkuvKTGEKsZ7Kk5958WTuwAdgekKE4A==
 *
 */
